package com.wzcl.app.service.impl;

import com.alibaba.fastjson.JSON;
import com.github.wxpay.sdk.WXPayConstants;
import com.google.gson.internal.$Gson$Preconditions;
import com.wzcl.app.config.MyWxPayConfig;
import com.wzcl.app.dao.model.*;
import com.wzcl.app.dao.repository.CourseRepository;
import com.wzcl.app.dao.repository.OrdersRepository;
import com.wzcl.app.dao.repository.RefundRequestRepository;
import com.wzcl.app.dao.repository.UserCourseRepository;
import com.wzcl.app.model.common.RVO;
import com.wzcl.app.service.AlipayService;
import com.wzcl.app.service.UserTopUpService;
import com.wzcl.app.service.WeChatPayService;
import com.wzcl.app.utils.DateUtil;
import com.wzcl.app.utils.StreamUtil;
import com.wzcl.app.utils.UuidUtil;
import com.wzcl.app.utils.WeChatPayUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import javax.net.ssl.SSLContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.math.BigDecimal;
import java.security.KeyStore;
import java.util.*;

/**
 * @author beingven
 * @Description 微信支付模块
 * @createTime 2020年12月03日 10:20:00
 */
@Service
@Slf4j
public class WeChatPayServiceImpl implements WeChatPayService {
    @Resource
    private MyWxPayConfig myWxPayConfig;

    @Resource
    private OrdersRepository ordersRepository;

    @Resource
    private RefundRequestRepository refundRequestRepository;

    @Resource
    private UserCourseRepository userCourseRepository;

    @Resource
    private CourseRepository courseRepository;

    @Resource
    private AlipayService alipayService;

    @Resource
    private UserTopUpService userTopUpService;

    @Resource
    private OrderServiceImpl orderService;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 微信统一下单方法（适用于扫码支付、APP支付、公众号支付）
     * @param orders 订单信息（至少包含订单金额与订单号）
     * @return 返回待支付二维码字符串
     */
    @Override
    public RVO createNativePay(Orders orders) {
        //判断订单是否存在
        if (ordersRepository.findAllByOrderNumber(orders.getNo()) == null){
            return RVO.error("该订单不存在");
        }
        //生成付款二维码金额乘以100
        Double money = orders.getPayMoney().doubleValue() *100;
        //去掉小数点的0
        String moneyStr = money.toString().substring(0, money.toString().indexOf('.'));
        //生成签名
        SortedMap<String,String> params = new TreeMap<>();
        params.put("appid",myWxPayConfig.getAppID());
        params.put("mch_id", myWxPayConfig.getMchID());
        params.put("nonce_str", UuidUtil.getUuid()); // 随机字符串，长度要求在32位以内
        //商品描述
        params.put("body","望子成龙教育-课程购买");
        params.put("out_trade_no",orders.getNo());
        //支付金额
        params.put("total_fee",moneyStr);
        // 调用微信支付API的ip，本地为127.0.0.1，服务端为服务器地址
        params.put("spbill_create_ip",myWxPayConfig.getSpbillCreateIp());
        // params.put("notify_url","http://young.free.idcfengye.com/weChat/native/notify");
        params.put("notify_url",myWxPayConfig.getPcNativeNotify());
        // 微信支付方式 native
        params.put("trade_type","NATIVE");
        //sign签名
        String sign = null;
        try {
            sign = WeChatPayUtil.generateSignature(params, myWxPayConfig.getKey(), WXPayConstants.SignType.MD5);
            log.info("签名生成："+sign);
            params.put("sign",sign);

            //map转xml
            String payXml = WeChatPayUtil.mapToXml(params);
            log.info("XML格式转换："+payXml);
            //统一下单
            String orderStr = WeChatPayUtil.sendPost(HttpClients.createDefault(), "https://api.mch.weixin.qq.com/pay/unifiedorder", payXml);
            log.info("统一下单："+orderStr);
            if(null == orderStr) {
                return null;
            }
            //XML转Map
            Map<String, String> unifiedOrderMap =  WeChatPayUtil.xmlToMap(orderStr);
            //判断订单是否重复
            if ("该订单已支付".equals(unifiedOrderMap.get("err_code_des"))){
                return RVO.error("该订单已支付,请勿重复支付");
            }
            //验证金额是否与数据库相等
            BigDecimal moneyFromDataBase = ordersRepository.findByOrderNo(orders.getNo()).getPayMoney();
            if (orders.getPayMoney().compareTo(moneyFromDataBase) != 0){
                return RVO.error("金额与数据库不一致");
            }
            if(unifiedOrderMap != null) {
                if (null == unifiedOrderMap.get("code_url")){
                    return RVO.error("无法获取二维码");
                }
                return RVO.success(unifiedOrderMap.get("code_url"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return RVO.error("获取页面数据失败");
    }


    public RVO createWebPay(Orders orders) throws Exception {
        //判断订单是否存在或者已经支付
        if (ordersRepository.findAllByOrderNumber(orders.getNo()) == null || ordersRepository.findAllByOrderNumber(orders.getNo()).getPayState() == 1){
            return RVO.error("该订单不存在或已支付");
        }
        // 生成付款二维码金额乘以100
        Double money = orders.getPayMoney().doubleValue() *100;
        // 去掉小数点的0
        SortedMap<String,String> params = new TreeMap<>();
        params.put("appid",myWxPayConfig.getAppID());
        params.put("mch_id", myWxPayConfig.getMchID());
        params.put("nonce_str", UuidUtil.getUuid()); // 随机字符串，长度要求在32位以内
        params.put("body","望子成龙教育-课程购买"); // 商品描述
        params.put("out_trade_no",orders.getNo());
        params.put("total_fee","1"); // 支付金额
        // 调用微信支付API的ip，本地为127.0.0.1，服务端为服务器地址
        params.put("spbill_create_ip",myWxPayConfig.getSpbillCreateIp());
        params.put("notify_url",myWxPayConfig.getPcNativeNotify());
        // 微信支付方式 native
        params.put("trade_type","MWEB");
        params.put("scene_info", "{" +
                "\"h5_info\":\""+"h5_info"+ "\"," +
                "\"type\":\"{" +
                    "\"wap_url\":\"" + "www.wzclzx.com"+"\"," +
                    "\"wap_name\":\"" + "望子成龙在线"+
                "}\"}");
        //sign签名
        String sign = WeChatPayUtil.generateSignature(params, myWxPayConfig.getKey(), WXPayConstants.SignType.MD5);
        params.put("sign",sign);
        //map转xml
        String payXml = WeChatPayUtil.mapToXml(params);
        log.info("XML格式转换："+payXml);
        //统一下单
        String orderStr = WeChatPayUtil.sendPost(HttpClients.createDefault(), "https://api.mch.weixin.qq.com/pay/unifiedorder", payXml);
        log.info("统一下单："+orderStr);
        if(null == orderStr) {
            return null;
        }
        //XML转Map
        Map<String, String> unifiedOrderMap =  WeChatPayUtil.xmlToMap(orderStr);
        //判断订单是否重复
        if ("该订单已支付".equals(unifiedOrderMap.get("err_code_des"))){
            return RVO.error("该订单已支付,请勿重复支付");
        }
        //验证金额是否与数据库相等
        BigDecimal moneyFromDataBase = ordersRepository.findByOrderNo(orders.getNo()).getPayMoney();
        if (moneyFromDataBase.compareTo(orders.getPayMoney()) != 0){
            return RVO.error("金额与数据库不一致");
        }
        if(unifiedOrderMap != null) {
            if (null == unifiedOrderMap.get("mweb_url")){
                return RVO.error("无法获取二维码");
            }
            return RVO.success(unifiedOrderMap.get("mweb_url"));
        }
        return RVO.error("无法获取二维码");
    }

    /**
     * 微信统一下单方法（适用于扫码支付、APP支付、公众号支付）异步回调
     * @param request 请求
     * @param response 响应
     * @throws Exception
     */
    @Override
    @Transactional
    public void nativeCallback(HttpServletRequest request, HttpServletResponse response) throws Exception {
        log.info("===========================异步通知开始===========================");
        InputStream inputStream =  request.getInputStream();
        BufferedReader in =  new BufferedReader(new InputStreamReader(inputStream,"UTF-8"));
        StringBuffer sb = new StringBuffer();
        String line;
        while ((line = in.readLine()) != null){
            sb.append(line);
        }
        in.close();
        inputStream.close();
        Map<String,String> callbackMap = WeChatPayUtil.xmlToMap(sb.toString());
        String resXml;
        String orderNumber = "";
        //判断签名是否正确
        if(WeChatPayUtil.isSignatureValid(callbackMap,myWxPayConfig.getKey())){
            if("SUCCESS".equals(callbackMap.get("result_code"))){
                //获取订单
                orderNumber = callbackMap.get("out_trade_no");
                Orders order = ordersRepository.findAllByOrderNumber(orderNumber);
                //判断是否是拼团订单
                if(order.getCreateFrom().contains("GROUP")){
                    ordersRepository.setPayStateAndOrdersStateByNo(5, 3, 1, orderNumber);
                    resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
                            + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml>";
                }else{
                    //支付成功，更新订单状态
                    ordersRepository.setPayStateAndOrdersStateByNo(1, 3, 1, orderNumber);
                    /**
                     * 添加用户课程
                     * 1、通过订单号拿到所有的商品id
                     * 2、根据商品id拿到所有课程
                     * 3、添加课程
                     */
                    List<UserCourse> list = new ArrayList<>();
                    List<OrderDetail> details = order.getDetails();
                    for (int index = 0; index < details.size(); index++) {
                        UserCourse userCourse = new UserCourse();
                        Course course = courseRepository.findAllByCourseId(details.get(index).getCommodity().getResourceId());
                        userCourse.setCourse(course);
                        userCourse.setUserId(order.getUserId());
                        userCourse.setUserPhone(order.getUserPhone());
                        list.add(userCourse);
                    }
                    userCourseRepository.saveAll(list);
                    resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
                            + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml>";
                }
            }
            else {
                resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
                        + "<return_msg><![CDATA[NULL]]></return_msg>" + "</xml>";
            }
            BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
            out.write(resXml.getBytes());
            out.flush();
            out.close();
            //如果是拼团订单还需要进行处理
            if (ordersRepository.findAllByOrderNumber(orderNumber).getCreateFrom().contains("GROUP")){
                orderService.OrderGroupConsumer(orderNumber);
            }
        }else {
            log.info("签名验证失败");
        }
    }

    /**
     * 申请退款
     * @param refundRequest 退款申请对象（包含orderId和退款申请信息）
     * @return 返回包含退款结果的RVO
     */
    @Transactional
    @Override
    public RVO applyRefund(RefundRequest refundRequest) throws InterruptedException {
        // 生成退款单号
        refundRequest.setOutRefundNo(UuidUtil.getNumberUuid());
        // 获取当前date时间
        refundRequest.setCreateTime(DateUtil.getDateBySystemTime(System.currentTimeMillis()));
        // 设置退款状态
        refundRequest.setAuditState(0);
        // 添加退款申请
        RefundRequest request = refundRequestRepository.save(refundRequest);
        // 更新订单为待退款
        ordersRepository.updateStateByOrderNumber(3, refundRequest.getOrderNo());
        return RVO.success(request);
    }

    /**
     * 退款审批
     * @param refundRequest 退款申请对象（包含审批状态）
     * @return 返回包含审核结果的RVO
     */
    @Transactional
    @Override
    public RVO createRefund(RefundRequest refundRequest) {
        if (refundRequest.getAuditState() == 1){
            log.info("准备退款");
            // 获取退款表信息
            RefundRequest request = refundRequestRepository.findByOrderNumber(refundRequest.getOrderNo());
            // 获取订单表信息
            Orders order = ordersRepository.findAllByOrderNumber(refundRequest.getOrderNo());
            Integer code = null;
            if (order.getSysType() == 1){
                //微信退款
                log.info("微信退款开始");
                code = createRefund(refundRequest.getOrderNo(), request.getOutRefundNo(), order.getPayMoney().doubleValue(), order.getPayMoney().doubleValue(), request.getReason());
            }
            if (order.getSysType() == 2){
                //支付宝退款
                log.info("支付宝退款开始");
                code = alipayService.createRefund(order.getNo());
            }
            if (order.getSysType() == 0){
                log.info("苹果退款开始");
                return userTopUpService.returnLearnCoin(refundRequest.getOrderNo(), refundRequest.getAuditState(), refundRequest.getMessage());
            }
            System.out.println(code);
            if (code == 200){
                log.info("退款成功");
                // 退款成功
                // 更新订单信息
                ordersRepository.updateStateByOrderNumber(4, order.getNo());
                // 更新退款申请表信息
                refundRequestRepository.updateAuditStateByOrderNumber(1, refundRequest.getMessage(), refundRequest.getOrderNo());
                // 删除对应课程
                List<OrderDetail> details = order.getDetails();
                System.out.println(order);
                System.out.println("有几个订单详情："+details.size());
                for (int index = 0; index < details.size(); index++) {
                    Course course = courseRepository.findAllByCourseId(details.get(index).getCommodity().getResourceId());
                    System.out.println("第"+(index+1)+"门课程:"+course);
                    userCourseRepository.deleteByCourseAndPhone(course.getId(), order.getUserPhone());
                }
                return RVO.success("退款成功");
            }
            //退款失败更新订单信息
            //更新订单信息
            ordersRepository.updateStateByOrderNumber(1, order.getNo());
            refundRequestRepository.updateAuditStateByOrderNumber(2, "退款失败", order.getNo());
        }
        if (refundRequest.getAuditState() == 2){
            refundRequestRepository.updateAuditStateByOrderNumber(2, refundRequest.getMessage(), refundRequest.getOrderNo());
            return RVO.success("退款已拒绝");
        }
        return RVO.error("传入参数异常！！！！！！！");
    }

    /**
     * 发起退款
     * @param outTradeNo 订单号
     * @param outRefundNo 退款订单号
     * @param totalFee 订单金额
     * @param refundFee 退款金额
     * @param refundDesc 退款原因
     * @return 返回包含退款结果的RVO
     */
    public Integer createRefund(String outTradeNo, String outRefundNo, Double totalFee, Double refundFee, String refundDesc){
        System.out.println("outTradeNo:"+outTradeNo);
        System.out.println("outRefundNo:"+outRefundNo);
        System.out.println("totalFee:"+totalFee);
        System.out.println("refundFee:"+refundFee);
        System.out.println("refundDesc:"+refundDesc);
        // 微信支付相关操作是以分为单位  乘以100 为实际消费与退款额度
        totalFee = totalFee *100;
        refundFee = refundFee *100;
        String total = totalFee.toString().substring(0, totalFee.toString().indexOf('.'));
        String refund = refundFee.toString().substring(0, refundFee.toString().indexOf('.'));
        try {
            //构建参数
            Map<String, String> dataMap = new HashMap<>();
            dataMap.put("appid",myWxPayConfig.getAppID());
            dataMap.put("mch_id",myWxPayConfig.getMchID());
            //自行实现该随机串
            dataMap.put("nonce_str",UuidUtil.getUuid());
            //商户订单号
            dataMap.put("out_trade_no",outTradeNo);
            //商户退款单号
            dataMap.put("out_refund_no",outRefundNo);
            //订单金额
            dataMap.put("total_fee",total);
            //退款金额
            dataMap.put("refund_fee",refund);
            //退款原因 (可写可不写)
            dataMap.put("refund_desc",refundDesc);
            //生成签名
            String sign = WeChatPayUtil.generateSignature(dataMap, myWxPayConfig.getKey(), WXPayConstants.SignType.MD5);
            dataMap.put("sign", sign);
            //map数据转xml
            String xmlString = WeChatPayUtil.mapToXml(dataMap);
            //读取微信证书并发起退款
            KeyStore keyStore = KeyStore.getInstance("PKCS12");
            //这里读取.p12的文件是放在服务器中 使用nginx配置时指向的后端项目路径
            FileInputStream inputStream = new FileInputStream(new File(myWxPayConfig.getCertPath()));//P12文件目录
            try {
                //这里写密码..默认是你的MCHID
                keyStore.load(inputStream, myWxPayConfig.getMchID().toCharArray());
            } finally {
                inputStream.close();
            }
            SSLContext sslcontext = SSLContexts.custom()
                    //这里也是写密码的
                    .loadKeyMaterial(keyStore, myWxPayConfig.getMchID().toCharArray())
                    .build();
            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
                    sslcontext,
                    SSLConnectionSocketFactory.getDefaultHostnameVerifier());
            CloseableHttpClient httpclient = HttpClients.custom()
                    .setSSLSocketFactory(sslsf)
                    .build();
            String jsonStr = null;
            try {
                HttpPost httpost = new HttpPost("https://api.mch.weixin.qq.com/secapi/pay/refund");
                httpost.setEntity(new StringEntity(xmlString, "UTF-8"));
                CloseableHttpResponse response = httpclient.execute(httpost);
                try {
                    HttpEntity entity = response.getEntity();
                    //接受到返回信息
                    jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8");
                    EntityUtils.consume(entity);
                } finally {
                    response.close();
                }
            } finally {
                httpclient.close();
            }
            //这里需要将发起退款的xml格式转换为map
            Map<String, String> returnMap = WeChatPayUtil.xmlToMap(jsonStr);
            log.info("退款结果："+returnMap);
            //用map取值返回的状态码来判断是否退款成功
            //判断是否退款成功
            if (returnMap.get("return_code").equals("SUCCESS")) {
                return  200;
            }
            return 500;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 500;
    }
}